import asyncio
import traceback

import wx
from loguru import logger
from serial import serialutil

from proto.firmata_pb2 import Broken
from .Firmd import FIRMATASession

_ = wx.GetTranslation

PLCObjDefaults = {
    "StartPLC": False,
    "GetTraceVariables": (Broken, None),
    "Get_PLC_status": (Broken, 0),
    "RemoteExec": (-1, "RemoteExec script failed!")
}
_SocketSession = None
err_count = 0


def _FIRMATA_connector_factory(uri, confnodesroot, **kwargs):
    """
    This returns the connector to YAPLC style PLCobject
    """

    servicetype, comportstr = uri.split("://")

    if confnodesroot:
        confnodesroot.logger.info(_("Connecting to:" + comportstr + "\n"))

    # AddToDoBeforeQuit = confnodesroot.AppFrame.AddToDoBeforeQuit
    lock = asyncio.Lock()

    def SocketSessionProcMapper(funcname):
        wampfuncname = funcname  # text('.'.join((ID, funcname)))

        async def catcher_func(*args, **kwargs):
            global err_count
            if _SocketSession is not None:
                try:
                    # logger.debug("%s in %s" % (wampfuncname, current_task().get_name()))
                    await lock.acquire()
                    res = await getattr(_SocketSession, wampfuncname)(*args, **kwargs)
                    err_count = 0
                    lock.release()
                    # logger.debug(f"{wampfuncname} returned")
                    return res
                except serialutil.SerialException as e:
                    confnodesroot.logger.write_error(str(e))
                except ConnectionResetError:
                    confnodesroot.logger.write_error(_("Connection lost!\n"))
                    await confnodesroot.CloseConnector()
                except RuntimeError as ex:
                    logger.error(traceback.format_exc())
                    lock.release()
                    raise ex
                except Exception as ex:
                    logger.error(traceback.format_exc())
                    err_count += 1
                    logger.debug(err_count)
                    if err_count >= 10 and 'close_async' != wampfuncname:
                        await confnodesroot.CloseConnector()
                        await getattr(_SocketSession, 'close_async')()
            lock.release()
            await confnodesroot.HidePLCProgress()
            return PLCObjDefaults.get(funcname)

        return catcher_func

    class SocketPLCObjectProxy(object):
        def __init__(self):
            global _SocketSession
            _SocketSession = FIRMATASession(confnodesroot, comportstr, **kwargs)

        def __getattr__(self, attrName):
            member = self.__dict__.get(attrName, None)
            if member is None:
                member = SocketSessionProcMapper(attrName)
                self.__dict__[attrName] = member
            return member

        # def __del__(self):
        #     '''调用者释放后执行'''
        #     _SocketSession.close()

    return SocketPLCObjectProxy()


FIRMATA_connector_factory = _FIRMATA_connector_factory

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    cnt = FIRMATA_connector_factory("FIRMATA://COM1", None)()
    loop.run_until_complete(cnt.Get_PLC_status())
